;(function(b) {
  var a = {}
  a.config = {
    editable: true,
    lineHeight: 15,
    basePath: '',
    rect: {
      attr: { x: 10, y: 10, width: 100, height: 50, r: 5, fill: '90-#fff-#C0C0C0', stroke: '#000', 'stroke-width': 1 },
      showType: 'image&text',
      type: 'state',
      name: { text: 'state', 'font-style': 'italic' },
      text: { text: '状态', 'font-size': 13 },
      margin: 5,
      props: [],
      img: {}
    },
    showPropsExtend: function(e, m, c) {
      // add by zhanglongfei
    },
    path: {
      attr: {
        path: { path: 'M10 10L100 100', stroke: '#808080', fill: 'none', 'stroke-width': 4 },
        arrow: { path: 'M10 10L10 10', stroke: '#808080', fill: '#808080', 'stroke-width': 4, radius: 6 },
        fromDot: { width: 5, height: 5, stroke: '#fff', fill: '#000', cursor: 'move', 'stroke-width': 2 },
        toDot: { width: 5, height: 5, stroke: '#fff', fill: '#000', cursor: 'move', 'stroke-width': 2 },
        bigDot: { width: 5, height: 5, stroke: '#fff', fill: '#000', cursor: 'move', 'stroke-width': 2 },
        smallDot: { width: 5, height: 5, stroke: '#fff', fill: '#000', cursor: 'move', 'stroke-width': 3 }
      },
      text: { text: 'TO {to}', cursor: 'move', background: '#000' },
      textPos: { x: 0, y: -10 },
      props: {
        // 自己添加 线的属性
        id: {
          name: 'id',
          label: 'Id',
          value: '',
          editor: function() {
            return new a.editors.readEditor()
          }
        },
        text: {
          name: 'text',
          label: '名称',
          value: '',
          editor: function() {
            return new a.editors.textEditor()
          }
        }
      }
    },
    tools: {
      attr: {},
      pointer: {},
      path: {},
      states: {
        onclick: function(c) {
          console.log(arguments)
        }
      },
      save: {
        onclick: function(c) {
          console.log('default:\n' + c)
        }
      }
    },
    props: { attr: {}, props: [] },
    restore: '',
    activeRects: { rects: [], rectAttr: { stroke: '#ff0000', 'stroke-width': 2 } },
    historyRects: { rects: [], pathAttr: { path: { stroke: '#00ff00' }, arrow: { stroke: '#00ff00', fill: '#00ff00' } } }
  }
  a.util = {
    isLine: function(g, f, e) {
      var d, c
      if (g.x - e.x == 0) {
        d = 1
      } else {
        d = (g.y - e.y) / (g.x - e.x)
      }
      c = (f.x - e.x) * d + e.y
      if (f.y - c < 10 && f.y - c > -10) {
        f.y = c
        return true
      }
      return false
    },
    center: function(d, c) {
      return { x: (d.x - c.x) / 2 + c.x, y: (d.y - c.y) / 2 + c.y }
    },
    nextId: (function() {
      var c = 0
      return function() {
        return ++c
      }
    })(),
    connPoint: function(j, d) {
      var c = d,
        e = { x: j.x + j.width / 2, y: j.y + j.height / 2 }
      var l = (e.y - c.y) / (e.x - c.x)
      l = isNaN(l) ? 0 : l
      var k = j.height / j.width
      var h = c.y < e.y ? -1 : 1,
        f = c.x < e.x ? -1 : 1,
        g,
        i
      if (Math.abs(l) > k && h == -1) {
        g = e.y - j.height / 2
        i = e.x + (h * j.height) / 2 / l
      } else {
        if (Math.abs(l) > k && h == 1) {
          g = e.y + j.height / 2
          i = e.x + (h * j.height) / 2 / l
        } else {
          if (Math.abs(l) < k && f == -1) {
            g = e.y + ((f * j.width) / 2) * l
            i = e.x - j.width / 2
          } else {
            if (Math.abs(l) < k && f == 1) {
              g = e.y + (j.width / 2) * l
              i = e.x + j.width / 2
            }
          }
        }
      }
      return { x: i, y: g }
    },
    arrow: function(l, k, d) {
      var g = Math.atan2(l.y - k.y, k.x - l.x) * (180 / Math.PI)
      var h = k.x - d * Math.cos(g * (Math.PI / 180))
      var f = k.y + d * Math.sin(g * (Math.PI / 180))
      var e = h + d * Math.cos((g + 120) * (Math.PI / 180))
      var j = f - d * Math.sin((g + 120) * (Math.PI / 180))
      var c = h + d * Math.cos((g + 240) * (Math.PI / 180))
      var i = f - d * Math.sin((g + 240) * (Math.PI / 180))
      return [k, { x: e, y: j }, { x: c, y: i }]
    }
  }
  a.rect = function(p, m, id) {
    //添加一个id参数，在构造节点时，如果有id则传入，如果没有则不用传入，系统自动分配一个
    var u = this,
      g,
      E = b.extend(true, {}, a.config.rect, p),
      C = m,
      t,
      e,
      n,
      f,
      x,
      v

    if (id) g = id
    else g = 'rect' + a.util.nextId()
    var rect_my = C.rect(E.attr.x, E.attr.y, E.attr.width, E.attr.height, E.attr.r)
    t = rect_my.hide().attr(E.attr)
    e = C.image(a.config.basePath + E.img.src, E.attr.x + E.img.width / 2, E.attr.y + (E.attr.height - E.img.height) / 2, E.img.width, E.img.height).hide()
    n = C.text(E.attr.x + E.img.width + (E.attr.width - E.img.width) / 2, E.attr.y + a.config.lineHeight / 2, E.name.text)
      .hide()
      .attr(E.name)
    f = C.text(E.attr.x + E.img.width + (E.attr.width - E.img.width) / 2, E.attr.y + (E.attr.height - a.config.lineHeight) / 2 + a.config.lineHeight, E.text.text)
      .hide()
      .attr(E.text)
    if (E.showType == 'text45') rect_my.rotate(45)
    t.drag(
      function(r, o) {
        A(r, o)
      },
      function() {
        z()
      },
      function() {
        l()
      }
    )
    e.drag(
      function(r, o) {
        A(r, o)
      },
      function() {
        z()
      },
      function() {
        l()
      }
    )
    n.drag(
      function(r, o) {
        A(r, o)
      },
      function() {
        z()
      },
      function() {
        l()
      }
    )
    f.drag(
      function(r, o) {
        A(r, o)
      },
      function() {
        z()
      },
      function() {
        l()
      }
    )
    var A = function(F, r) {
      if (!a.config.editable) {
        return
      }
      var o = x + F
      var G = v + r
      q.x = o - E.margin
      q.y = G - E.margin
      B()
    }
    var z = function() {
      x = t.attr('x')
      v = t.attr('y')
      t.attr({ opacity: 0.5 })
      e.attr({ opacity: 0.5 })
      f.attr({ opacity: 0.5 })
    }
    var l = function() {
      t.attr({ opacity: 1 })
      e.attr({ opacity: 1 })
      f.attr({ opacity: 1 })
    }
    var s,
      i = {},
      h = 5,
      q = { x: E.attr.x - E.margin, y: E.attr.y - E.margin, width: E.attr.width + E.margin * 2, height: E.attr.height + E.margin * 2 }
    s = C.path('M0 0L1 1').hide()
    i.t = C.rect(0, 0, h, h)
      .attr({ fill: '#000', stroke: '#fff', cursor: 's-resize' })
      .hide()
      .drag(
        function(r, o) {
          D(r, o, 't')
        },
        function() {
          k(this.attr('x') + h / 2, this.attr('y') + h / 2, 't')
        },
        function() {}
      )
    i.lt = C.rect(0, 0, h, h)
      .attr({ fill: '#000', stroke: '#fff', cursor: 'nw-resize' })
      .hide()
      .drag(
        function(r, o) {
          D(r, o, 'lt')
        },
        function() {
          k(this.attr('x') + h / 2, this.attr('y') + h / 2, 'lt')
        },
        function() {}
      )
    i.l = C.rect(0, 0, h, h)
      .attr({ fill: '#000', stroke: '#fff', cursor: 'w-resize' })
      .hide()
      .drag(
        function(r, o) {
          D(r, o, 'l')
        },
        function() {
          k(this.attr('x') + h / 2, this.attr('y') + h / 2, 'l')
        },
        function() {}
      )
    i.lb = C.rect(0, 0, h, h)
      .attr({ fill: '#000', stroke: '#fff', cursor: 'sw-resize' })
      .hide()
      .drag(
        function(r, o) {
          D(r, o, 'lb')
        },
        function() {
          k(this.attr('x') + h / 2, this.attr('y') + h / 2, 'lb')
        },
        function() {}
      )
    i.b = C.rect(0, 0, h, h)
      .attr({ fill: '#000', stroke: '#fff', cursor: 's-resize' })
      .hide()
      .drag(
        function(r, o) {
          D(r, o, 'b')
        },
        function() {
          k(this.attr('x') + h / 2, this.attr('y') + h / 2, 'b')
        },
        function() {}
      )
    i.rb = C.rect(0, 0, h, h)
      .attr({ fill: '#000', stroke: '#fff', cursor: 'se-resize' })
      .hide()
      .drag(
        function(r, o) {
          D(r, o, 'rb')
        },
        function() {
          k(this.attr('x') + h / 2, this.attr('y') + h / 2, 'rb')
        },
        function() {}
      )
    i.r = C.rect(0, 0, h, h)
      .attr({ fill: '#000', stroke: '#fff', cursor: 'w-resize' })
      .hide()
      .drag(
        function(r, o) {
          D(r, o, 'r')
        },
        function() {
          k(this.attr('x') + h / 2, this.attr('y') + h / 2, 'r')
        },
        function() {}
      )
    i.rt = C.rect(0, 0, h, h)
      .attr({ fill: '#000', stroke: '#fff', cursor: 'ne-resize' })
      .hide()
      .drag(
        function(r, o) {
          D(r, o, 'rt')
        },
        function() {
          k(this.attr('x') + h / 2, this.attr('y') + h / 2, 'rt')
        },
        function() {}
      )
    var D = function(F, r, G) {
      if (!a.config.editable) {
        return
      }
      var o = _bx + F,
        H = _by + r
      switch (G) {
        case 't':
          q.height += q.y - H
          q.y = H
          break
        case 'lt':
          q.width += q.x - o
          q.height += q.y - H
          q.x = o
          q.y = H
          break
        case 'l':
          q.width += q.x - o
          q.x = o
          break
        case 'lb':
          q.height = H - q.y
          q.width += q.x - o
          q.x = o
          break
        case 'b':
          q.height = H - q.y
          break
        case 'rb':
          q.height = H - q.y
          q.width = o - q.x
          break
        case 'r':
          q.width = o - q.x
          break
        case 'rt':
          q.width = o - q.x
          q.height += q.y - H
          q.y = H
          break
      }
      B()
    }
    var k = function(r, o, F) {
      _bx = r
      _by = o
    }
    b([t.node, f.node, n.node, e.node]).bind('click', function() {
      if (!a.config.editable) {
        return
      }
      w()
      var o = b(C).data('mod')
      switch (o) {
        case 'pointer':
          break
        case 'path':
          var r = b(C).data('currNode')
          if (r && r.getId() != g && r.getId().substring(0, 4) == 'rect') {
            b(C).trigger('addpath', [r, u])
          }
          break
      }
      b(C).trigger('click', u)
      b(C).data('currNode', u)
      return false
    })
    var j = function(o, r) {
      if (!a.config.editable) {
        return
      }
      if (r.getId() == g) {
        b(C).trigger('showprops', [E.props, r])
      } else {
        d()
      }
    }
    b(C).bind('click', j)
    var c = function(o, F, r) {
      if (r.getId() == g) {
        f.attr({ text: F })
      }
    }
    b(C).bind('textchange', c)
    function y() {
      return 'M' + q.x + ' ' + q.y + 'L' + q.x + ' ' + (q.y + q.height) + 'L' + (q.x + q.width) + ' ' + (q.y + q.height) + 'L' + (q.x + q.width) + ' ' + q.y + 'L' + q.x + ' ' + q.y
    }
    function w() {
      s.show()
      for (var o in i) {
        i[o].show()
      }
    }
    function d() {
      s.hide()
      for (var o in i) {
        i[o].hide()
      }
    }
    function B() {
      var F = q.x + E.margin,
        r = q.y + E.margin,
        G = q.width - E.margin * 2,
        o = q.height - E.margin * 2
      t.attr({ x: F, y: r, width: G, height: o })
      switch (E.showType) {
        case 'image':
          e.attr({ x: F + (G - E.img.width) / 2, y: r + (o - E.img.height) / 2 }).show()
          break
        case 'text':
          t.show()
          f.attr({ x: F + G / 2, y: r + o / 2 }).show()
          break
        case 'text45':
          t.show()
          f.attr({ x: F + G / 2, y: r + o / 2 }).show()
          break
        case 'image&text':
          t.show()
          n.attr({ x: F + E.img.width + (G - E.img.width) / 2, y: r + a.config.lineHeight / 2 }).show()
          f.attr({ x: F + E.img.width + (G - E.img.width) / 2, y: r + (o - a.config.lineHeight) / 2 + a.config.lineHeight }).show()
          e.attr({ x: F + E.img.width / 2, y: r + (o - E.img.height) / 2 }).show()
          break
      }
      i.t.attr({ x: q.x + q.width / 2 - h / 2, y: q.y - h / 2 })
      i.lt.attr({ x: q.x - h / 2, y: q.y - h / 2 })
      i.l.attr({ x: q.x - h / 2, y: q.y - h / 2 + q.height / 2 })
      i.lb.attr({ x: q.x - h / 2, y: q.y - h / 2 + q.height })
      i.b.attr({ x: q.x - h / 2 + q.width / 2, y: q.y - h / 2 + q.height })
      i.rb.attr({ x: q.x - h / 2 + q.width, y: q.y - h / 2 + q.height })
      i.r.attr({ x: q.x - h / 2 + q.width, y: q.y - h / 2 + q.height / 2 })
      i.rt.attr({ x: q.x - h / 2 + q.width, y: q.y - h / 2 })
      s.attr({ path: y() })
      b(C).trigger('rectresize', u)
    }
    this.toJson = function() {
      var r = "{type:'" + E.type + "',text:{text:'" + f.attr('text') + "'}, attr:{ x:" + Math.round(t.attr('x')) + ', y:' + Math.round(t.attr('y')) + ', width:' + Math.round(t.attr('width')) + ', height:' + Math.round(t.attr('height')) + '}, props:{'
      for (var o in E.props) {
        r += o + ":{value:'" + E.props[o].value + "'},"
      }
      if (r.substring(r.length - 1, r.length) == ',') {
        r = r.substring(0, r.length - 1)
      }
      r += '}}'
      return r
    }
    this.restore = function(o) {
      var r = o
      E = b.extend(true, E, o)
      f.attr({ text: r.text.text })
      B()
    }
    this.getBBox = function() {
      return q
    }
    this.getId = function() {
      return g
    }
    this.remove = function() {
      t.remove()
      f.remove()
      n.remove()
      e.remove()
      s.remove()
      for (var o in i) {
        i[o].remove()
      }
    }
    this.text = function() {
      return f.attr('text')
    }
    this.attr = function(o) {
      if (o) {
        t.attr(o)
      }
    }
    B()
  }
  a.path = function(q, n, u, e, id) {
    //添加一个id参数，在构造节点时，如果有id则传入，如果没有则不用传入，系统自动分配一个
    var v = this,
      z = n,
      B = b.extend(true, {}, a.config.path),
      i,
      t,
      f,
      h = B.textPos,
      y,
      w,
      k = u,
      s = e,
      g,
      x
    if (id) g = id
    else g = 'path' + a.util.nextId()
    function p(G, H, D, L) {
      var F = this,
        M = G,
        r,
        o = D,
        O = L,
        K,
        I,
        N = H
      switch (M) {
        case 'from':
          r = z.rect(H.x - B.attr.fromDot.width / 2, H.y - B.attr.fromDot.height / 2, B.attr.fromDot.width, B.attr.fromDot.height).attr(B.attr.fromDot)
          break
        case 'big':
          r = z.rect(H.x - B.attr.bigDot.width / 2, H.y - B.attr.bigDot.height / 2, B.attr.bigDot.width, B.attr.bigDot.height).attr(B.attr.bigDot)
          break
        case 'small':
          r = z.rect(H.x - B.attr.smallDot.width / 2, H.y - B.attr.smallDot.height / 2, B.attr.smallDot.width, B.attr.smallDot.height).attr(B.attr.smallDot)
          break
        case 'to':
          r = z.rect(H.x - B.attr.toDot.width / 2, H.y - B.attr.toDot.height / 2, B.attr.toDot.width, B.attr.toDot.height).attr(B.attr.toDot)
          break
      }
      if (r && (M == 'big' || M == 'small')) {
        r.drag(
          function(Q, P) {
            C(Q, P)
          },
          function() {
            J()
          },
          function() {
            E()
          }
        )
        var C = function(R, Q) {
          var P = K + R,
            S = I + Q
          F.moveTo(P, S)
        }
        var J = function() {
          if (M == 'big') {
            K = r.attr('x') + B.attr.bigDot.width / 2
            I = r.attr('y') + B.attr.bigDot.height / 2
          }
          if (M == 'small') {
            K = r.attr('x') + B.attr.smallDot.width / 2
            I = r.attr('y') + B.attr.smallDot.height / 2
          }
        }
        var E = function() {}
      }
      this.type = function(P) {
        if (P) {
          M = P
        } else {
          return M
        }
      }
      this.node = function(P) {
        if (P) {
          r = P
        } else {
          return r
        }
      }
      this.left = function(P) {
        if (P) {
          o = P
        } else {
          return o
        }
      }
      this.right = function(P) {
        if (P) {
          O = P
        } else {
          return O
        }
      }
      this.remove = function() {
        o = null
        O = null
        r.remove()
      }
      this.pos = function(P) {
        if (P) {
          N = P
          r.attr({ x: N.x - r.attr('width') / 2, y: N.y - r.attr('height') / 2 })
          return this
        } else {
          return N
        }
      }
      this.moveTo = function(Q, T) {
        this.pos({ x: Q, y: T })
        switch (M) {
          case 'from':
            if (O && O.right() && O.right().type() == 'to') {
              O.right().pos(a.util.connPoint(s.getBBox(), N))
            }
            if (O && O.right()) {
              O.pos(a.util.center(N, O.right().pos()))
            }
            break
          case 'big':
            if (O && O.right() && O.right().type() == 'to') {
              O.right().pos(a.util.connPoint(s.getBBox(), N))
            }
            if (o && o.left() && o.left().type() == 'from') {
              o.left().pos(a.util.connPoint(k.getBBox(), N))
            }
            if (O && O.right()) {
              O.pos(a.util.center(N, O.right().pos()))
            }
            if (o && o.left()) {
              o.pos(a.util.center(N, o.left().pos()))
            }
            var S = { x: N.x, y: N.y }
            if (a.util.isLine(o.left().pos(), S, O.right().pos())) {
              M = 'small'
              r.attr(B.attr.smallDot)
              this.pos(S)
              var P = o
              o.left().right(o.right())
              o = o.left()
              P.remove()
              var R = O
              O.right().left(O.left())
              O = O.right()
              R.remove()
            }
            break
          case 'small':
            if (o && O && !a.util.isLine(o.pos(), { x: N.x, y: N.y }, O.pos())) {
              M = 'big'
              r.attr(B.attr.bigDot)
              var P = new p('small', a.util.center(o.pos(), N), o, o.right())
              o.right(P)
              o = P
              var R = new p('small', a.util.center(O.pos(), N), O.left(), O)
              O.left(R)
              O = R
            }
            break
          case 'to':
            if (o && o.left() && o.left().type() == 'from') {
              o.left().pos(a.util.connPoint(k.getBBox(), N))
            }
            if (o && o.left()) {
              o.pos(a.util.center(N, o.left().pos()))
            }
            break
        }
        m()
      }
    }
    function j() {
      var D,
        C,
        E = k.getBBox(),
        F = s.getBBox(),
        r,
        o
      r = a.util.connPoint(E, { x: F.x + F.width / 2, y: F.y + F.height / 2 })
      o = a.util.connPoint(F, r)
      D = new p('from', r, null, new p('small', { x: (r.x + o.x) / 2, y: (r.y + o.y) / 2 }))
      D.right().left(D)
      C = new p('to', o, D.right(), null)
      D.right().right(C)
      this.toPathString = function() {
        if (!D) {
          return ''
        }
        var J = D,
          I = 'M' + J.pos().x + ' ' + J.pos().y,
          H = ''
        while (J.right()) {
          J = J.right()
          I += 'L' + J.pos().x + ' ' + J.pos().y
        }
        var G = a.util.arrow(J.left().pos(), J.pos(), B.attr.arrow.radius)
        H = 'M' + G[0].x + ' ' + G[0].y + 'L' + G[1].x + ' ' + G[1].y + 'L' + G[2].x + ' ' + G[2].y + 'z'
        return [I, H]
      }
      this.toJson = function() {
        var G = '[',
          H = D
        while (H) {
          if (H.type() == 'big') {
            G += '{x:' + Math.round(H.pos().x) + ',y:' + Math.round(H.pos().y) + '},'
          }
          H = H.right()
        }
        if (G.substring(G.length - 1, G.length) == ',') {
          G = G.substring(0, G.length - 1)
        }
        G += ']'
        return G
      }
      this.restore = function(H) {
        var I = H,
          J = D.right()
        for (var G = 0; G < I.length; G++) {
          J.moveTo(I[G].x, I[G].y)
          J.moveTo(I[G].x, I[G].y)
          J = J.right()
        }
        this.hide()
      }
      this.fromDot = function() {
        return D
      }
      this.toDot = function() {
        return C
      }
      this.midDot = function() {
        var H = D.right(),
          G = D.right().right()
        while (G.right() && G.right().right()) {
          G = G.right().right()
          H = H.right()
        }
        return H
      }
      this.show = function() {
        var G = D
        while (G) {
          G.node().show()
          G = G.right()
        }
      }
      this.hide = function() {
        var G = D
        while (G) {
          G.node().hide()
          G = G.right()
        }
      }
      this.remove = function() {
        var G = D
        while (G) {
          if (G.right()) {
            G = G.right()
            G.left().remove()
          } else {
            G.remove()
            G = null
          }
        }
      }
    }
    B = b.extend(true, B, q)
    i = z.path(B.attr.path.path).attr(B.attr.path)
    t = z.path(B.attr.arrow.path).attr(B.attr.arrow)
    x = new j()
    x.hide()
    f = z
      .text(0, 0, B.text.text)
      .attr(B.text)
      .attr({ text: B.text.text.replace('{from}', k.text()).replace('{to}', s.text()) })
    f.drag(
      function(r, o) {
        if (!a.config.editable) {
          return
        }
        f.attr({ x: y + r, y: w + o })
      },
      function() {
        y = f.attr('x')
        w = f.attr('y')
      },
      function() {
        var o = x.midDot().pos()
        h = { x: f.attr('x') - o.x, y: f.attr('y') - o.y }
      }
    )
    m()
    b([i.node, t.node]).bind('click', function() {
      if (!a.config.editable) {
        return
      }
      b(z).trigger('click', v)
      b(z).data('currNode', v)
      return false
    })
    var l = function(r, C) {
      if (!a.config.editable) {
        return
      }
      if (C && C.getId() == g) {
        x.show()
        b(z).trigger('showprops', [B.props, v])
      } else {
        x.hide()
      }
      var o = b(z).data('mod')
      switch (o) {
        case 'pointer':
          break
        case 'path':
          break
      }
    }
    b(z).bind('click', l)
    var A = function(o, r) {
      if (!a.config.editable) {
        return
      }
      if (r && (r.getId() == k.getId() || r.getId() == s.getId())) {
        b(z).trigger('removepath', v)
      }
    }
    b(z).bind('removerect', A)
    var d = function(C, D) {
      if (!a.config.editable) {
        return
      }
      if (k && k.getId() == D.getId()) {
        var o
        if (
          x
            .fromDot()
            .right()
            .right()
            .type() == 'to'
        ) {
          o = { x: s.getBBox().x + s.getBBox().width / 2, y: s.getBBox().y + s.getBBox().height / 2 }
        } else {
          o = x
            .fromDot()
            .right()
            .right()
            .pos()
        }
        var r = a.util.connPoint(k.getBBox(), o)
        x.fromDot().moveTo(r.x, r.y)
        m()
      }
      if (s && s.getId() == D.getId()) {
        var o
        if (
          x
            .toDot()
            .left()
            .left()
            .type() == 'from'
        ) {
          o = { x: k.getBBox().x + k.getBBox().width / 2, y: k.getBBox().y + k.getBBox().height / 2 }
        } else {
          o = x
            .toDot()
            .left()
            .left()
            .pos()
        }
        var r = a.util.connPoint(s.getBBox(), o)
        x.toDot().moveTo(r.x, r.y)
        m()
      }
    }
    b(z).bind('rectresize', d)
    var c = function(r, o, C) {
      if (C.getId() == g) {
        f.attr({ text: o })
      }
    }
    b(z).bind('textchange', c)
    this.from = function() {
      return k
    }
    this.to = function() {
      return s
    }
    this.toJson = function() {
      var r = "{from:'" + k.getId() + "',to:'" + s.getId() + "', dots:" + x.toJson() + ",text:{text:'" + f.attr('text') + "'},textPos:{x:" + Math.round(h.x) + ',y:' + Math.round(h.y) + '}, props:{'
      for (var o in B.props) {
        r += o + ":{value:'" + B.props[o].value + "'},"
      }
      if (r.substring(r.length - 1, r.length) == ',') {
        r = r.substring(0, r.length - 1)
      }
      r += '}}'
      return r
    }
    this.restore = function(o) {
      var r = o
      B = b.extend(true, B, o)
      x.restore(r.dots)
    }
    this.remove = function() {
      x.remove()
      i.remove()
      t.remove()
      f.remove()
      try {
        b(z).unbind('click', l)
      } catch (o) {}
      try {
        b(z).unbind('removerect', A)
      } catch (o) {}
      try {
        b(z).unbind('rectresize', d)
      } catch (o) {}
      try {
        b(z).unbind('textchange', c)
      } catch (o) {}
    }
    function m() {
      var r = x.toPathString(),
        o = x.midDot().pos()
      i.attr({ path: r[0] })
      t.attr({ path: r[1] })
      f.attr({ x: o.x + h.x, y: o.y + h.y })
    }
    this.getId = function() {
      return g
    }
    this.text = function() {
      return f.attr('text')
    }
    this.attr = function(o) {
      if (o && o.path) {
        i.attr(o.path)
      }
      if (o && o.arrow) {
        t.attr(o.arrow)
      }
    }
  }
  a.props = function(h, f) {
    // zhanglongfei modify
    b('#myflow_props')
      .hide()
      .draggable({ handle: '#myflow_props_handle' })
      .resizable()
      .css(a.config.props.attr)
      .bind('click', function() {
        return false
      })
    if (typeof a.config.showPropsExtend !== 'undefined') {
      b(f).bind('showprops', a.config.showPropsExtend)
    }

    // var j = this, c = b("#myflow_props").hide().draggable({ handle: "#myflow_props_handle" }).resizable().css(a.config.props.attr).bind("click", function () {
    //     return false
    // }), e = c.find("table"), g = f, i;

    // var d = function (n, m, o) {
    //     if (i && i.getId() == o.getId()) {
    //         return
    //     }

    //     i = o;
    //     b(e).find(".editor").each(function () {
    //         var k = b(this).data("editor");
    //         if (k) {
    //             k.destroy()
    //         }
    //     });
    //     e.empty();
    //     c.show();
    //     for (var l in m) {
    //         e.append("<tr><th>" + m[l].label + '</th><td><div id="p' + l + '" class="editor"></div></td></tr>');
    //         if (m[l].editor) {
    //             m[l].editor().init(m, l, "p" + l, o, g)
    //         }
    //     }
    //     // showPropsExtend(e,m,c);
    // };
    // b(g).bind("showprops", d)
  }
  a.editors = {
    textEditor: function() {
      var d, e, c, g, f
      this.init = function(i, h, m, l, j) {
        d = i
        e = h
        c = m
        g = l
        f = j
        b('<input  style="width:100%;"/>')
          .val(g.text())
          .change(function() {
            i[e].value = b(this).val()
            b(f).trigger('textchange', [b(this).val(), g])
          })
          .appendTo('#' + c)
        b('#' + c).data('editor', this)
      }
      this.destroy = function() {
        b('#' + c + ' input').each(function() {
          d[e].value = b(this).val()
          b(f).trigger('textchange', [b(this).val(), g])
        })
      }
    },
    //添加一个自定义的只读编辑框
    readEditor: function() {
      var _props, _k, _div, _src, _r
      this.init = function(props, k, div, src, r) {
        _props = props
        _k = k
        _div = div
        _src = src
        _r = r
        $('<input style="width:100%;" readonly />')
          .val(props[_k].value)
          .change(function() {
            props[_k].value = $(this).val()
          })
          .appendTo('#' + _div)

        $('#' + _div).data('editor', this)
      }
      this.destroy = function() {
        $('#' + _div + ' input').each(function() {
          _props[_k].value = $(this).val()
        })
      }
    }
  }
  a.init = function(x, r) {
    var v = b(window).width(),
      e = b(window).height(),
      y = Raphael(x, v * 1.5, e * 1.5),
      q = {},
      g = {}
    b.extend(true, a.config, r)
    if (r.activeRects) {
      a.config.activeRects.rects = r.activeRects.rects
    } else {
      a.config.activeRects.rects = []
    }
    if (r.historyRects) {
      a.config.historyRects.rects = r.historyRects.rects
    } else {
      a.config.historyRects.rects = []
    }
    b(document).keydown(function(i, byButton) {
      if (!a.config.editable) {
        return
      }
      if (i.keyCode == 46 || byButton) {
        //
        var j = b(y).data('currNode')
        if (j) {
          if (j.getId().substring(0, 4) == 'rect') {
            b(y).trigger('removerect', j)
          } else {
            if (j.getId().substring(0, 4) == 'path') {
              b(y).trigger('removepath', j)
            }
          }
          b(y).removeData('currNode')
        }
      }
    })
    b(document).click(function() {
      b(y).data('currNode', null)
      b(y).trigger('click', {
        getId: function() {
          return '00000000'
        }
      })
      b(y).trigger('showprops', [
        a.config.props.props,
        {
          getId: function() {
            return '00000000'
          }
        }
      ])
    })
    var w = function(c, i) {
      if (!a.config.editable) {
        return
      }
      if (i.getId().substring(0, 4) == 'rect') {
        q[i.getId()] = null
        i.remove()
      } else {
        if (i.getId().substring(0, 4) == 'path') {
          g[i.getId()] = null
          i.remove()
        }
      }
    }
    b(y).bind('removepath', w)
    b(y).bind('removerect', w)
    b(y).bind('addrect', function(j, c, k) {
      var i = new a.rect(b.extend(true, {}, a.config.tools.states[c], k), y)
      q[i.getId()] = i
    })
    var f = function(i, k, j) {
      var c = new a.path({}, y, k, j)
      g[c.getId()] = c
    }
    b(y).bind('addpath', f)
    b(y).data('mod', 'point')
    if (a.config.editable) {
      b('#myflow_tools')
        .draggable({ handle: '#myflow_tools_handle' })
        .css(a.config.tools.attr)
      b('#myflow_tools .node').hover(
        function() {
          b(this).addClass('mover')
        },
        function() {
          b(this).removeClass('mover')
        }
      )
      b('#myflow_tools .selectable').click(function() {
        b('.selected').removeClass('selected')
        b(this).addClass('selected')
        b(y).data('mod', this.id)
      })
      b('#myflow_tools .state').each(function() {
        b(this).draggable({ helper: 'clone' })
      })
      b(x).droppable({
        accept: '.state',
        drop: function(c, i) {
          // add by zhanglongfei,由于新系统是单页面应用，所以需要去掉上方和左侧的位置
          var $selfOffset = b(this).offset()
          var xPosition = i.helper.offset().left - $selfOffset.left
          var yPosition = i.helper.offset().top - $selfOffset.top
          b(y).trigger('addrect', [i.helper.attr('type'), { attr: { x: xPosition, y: yPosition } }])
          // b(y).trigger("addrect", [i.helper.attr("type"), { attr: { x: i.helper.offset().left, y: i.helper.offset().top } }])
        }
      })
      b('#myflow_save').click(function() {
        var i = '{states:{'
        for (var c in q) {
          if (q[c]) {
            i += q[c].getId() + ':' + q[c].toJson() + ','
          }
        }
        if (i.substring(i.length - 1, i.length) == ',') {
          i = i.substring(0, i.length - 1)
        }
        i += '},paths:{'
        for (var c in g) {
          if (g[c]) {
            i += g[c].getId() + ':' + g[c].toJson() + ','
          }
        }
        if (i.substring(i.length - 1, i.length) == ',') {
          i = i.substring(0, i.length - 1)
        }
        i += '},props:{props:{'
        for (var c in a.config.props.props) {
          i += c + ":{value:'" + a.config.props.props[c].value + "'},"
        }
        if (i.substring(i.length - 1, i.length) == ',') {
          i = i.substring(0, i.length - 1)
        }
        i += '}}}'
        a.config.tools.save.onclick(i)
      })
      new a.props({}, y)
    }
    if (r.restore) {
      var B = r.restore
      var z = {}
      if (B.states) {
        for (var s in B.states) {
          var d = new a.rect(b.extend(true, {}, a.config.tools.states[B.states[s].type], B.states[s]), y, s)
          d.restore(B.states[s])
          z[s] = d
          q[d.getId()] = d
        }
      }
      if (B.paths) {
        for (var s in B.paths) {
          var n = new a.path(b.extend(true, {}, a.config.tools.path, B.paths[s]), y, z[B.paths[s].from], z[B.paths[s].to], s)
          n.restore(B.paths[s])
          g[n.getId()] = n
        }
      }
    }
    //设置历史、当前的节点和线
    var historyRects = a.config.historyRects,
      activeRects = a.config.activeRects
    if (historyRects.rects.length || activeRects.rects.length) {
      var m = {},
        z = {}
      for (var h in g) {
        if (!z[g[h].from().getId()]) {
          z[g[h].from().getId()] = { rect: g[h].from(), paths: {} }
        }
        z[g[h].from().getId()].paths[g[h].getId()] = g[h]
        if (!z[g[h].to().getId()]) {
          z[g[h].to().getId()] = { rect: g[h].to(), paths: {} }
        }
      }
      for (var u = 0; u < historyRects.rects.length; u++) {
        if (z[historyRects.rects[u].code]) {
          z[historyRects.rects[u].code].rect.attr(historyRects.rectAttr)
        }
        for (var t = 0; t < historyRects.rects[u].paths.length; t++) {
          if (z[historyRects.rects[u].code].paths[historyRects.rects[u].paths[t]]) {
            z[historyRects.rects[u].code].paths[historyRects.rects[u].paths[t]].attr(historyRects.pathAttr)
          }
        }
      }
      for (var u = 0; u < activeRects.rects.length; u++) {
        if (z[activeRects.rects[u].code]) {
          z[activeRects.rects[u].code].rect.attr(activeRects.rectAttr)
        }
        for (var t = 0; t < activeRects.rects[u].paths.length; t++) {
          if (z[activeRects.rects[u].code].paths[activeRects.rects[u].paths[t]]) {
            z[activeRects.rects[u].code].paths[activeRects.rects[u].paths[t]].attr(activeRects.pathAttr)
          }
        }
      }
    }
  }
  b.fn.myflow = function(c) {
    return this.each(function() {
      a.init(this, c)
    })
  }
  b.myflow = a
})(jQuery)
